/*
 *
 *
Copyright (c) 1998-2001 Aristar, Inc. All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

3. The end-user documentation included with the redistribution, if any,
must include the following acknowledgment:

"This product includes software developed by Aristar, Inc. (http://www.aristar.com/)."

Alternately, this acknowledgment may appear in the software itself, if and
wherever such third-party acknowledgments normally appear.

4. The names "Dialect" and "Aristar, Inc." must not be used to endorse or
promote products derived from this software without prior written permission.
For written permission, please contact info@aristar.com.

5. Products derived from this software may not be called "Dialect", nor may
"Dialect" appear in their name, without prior written permission of Aristar, Inc.

THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
ARISTAR, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

This software consists of voluntary contributions made by many individuals
on behalf of Aristar, Inc.  For more information on Aristar, Inc.,
please see <http://www.aristar.com/>.

Portions of this software are based upon software freely available and
written by individuals not associated with Aristar, Inc.  Copyrights are
maintained in the relevant source code files.
 *
 *
 */

// DialectApp.cpp : Defines the class behaviors for the application.
//

#include "stdafx.h"
#include "DialectApp.h"
#include "MainFrm.h"
#include "InitPathDlg.h"

#ifdef DIALECT_ENT
#	include "LayoutLib.h"
#endif

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

// Shared interpreter
#if defined(DIALECT_ENT)
CRuleInterp g_Interpreter;
#else
CDialectInterp g_Interpreter;
#endif

/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////

extern VALUE g_vCmdLineArray;
extern VALUE g_vCmdLineString;

//class CCmdLine : public CCommandLineInfo
//{
//	virtual void ParseParam(LPCTSTR lpszParam,BOOL bFlag,BOOL bLast);
//public:
	static CString dataDir;		// Data directory
	static CString initScript;		// Initial script and path
	static CString openMe;	// Text file to open
//};

static void ParseParam(LPCTSTR lpszParam/*,BOOL bFlag,BOOL bLast*/)
{
	static CString lastFlag;
	static bNoOpenMe = false;

	bool bFlag = *lpszParam == '-';

	int index = g_vCmdLineArray.Length() + 1;
	g_vCmdLineArray.Resize(index);

	if (bFlag) {
		lastFlag = lpszParam;
		g_vCmdLineArray.Set(index, lpszParam);
	}
	else {
		g_vCmdLineArray.Set(index, lpszParam);

		if (lastFlag.CompareNoCase(_T("-run")) == 0) {
			initScript = lpszParam;
			bNoOpenMe = true;
		}
		else {
			if (openMe.IsEmpty() && !bNoOpenMe) openMe = lpszParam;	// Take first
		}

		lastFlag.Empty();	// Flags only last one round
	}
}

static void ParseElCmdLineO(LPCTSTR psz)
{
	// The CE version of the MFC command line parsing
	// failed to correctly group items inside double-quotes
	// So we are going to parse the thing ourselves.
	CString item;
	while (*psz) {
		item.Empty();
		while ((*psz != '\0') && (*psz <= ' ')) ++psz; // Eat white space
		if (*psz == '\0') break;
		else if (*psz == '"') {
			++psz;
			while ((*psz != '\0') && (*psz != '"')) {
				item += *psz++;
			}
			ParseParam(item);
			if (*psz == '"') ++psz;
		}
		else {
			while ((*psz != '\0') && (*psz > ' ')) {
				item += *psz++;
			}
			ParseParam(item);
		}
	}
}

/////////////////////////////////////////////////////////////////////////////

void SplitFullPath(CString input, CString& directory, CString& filename)
{
	directory.Empty();
	filename.Empty();

	if (!input.IsEmpty()) {
		int pos = input.ReverseFind('\\');
		if (pos >= 0) {
			filename = input.Mid(pos + 1);
			directory = input.Left(pos + 1);	// Leave slash
		}
		else {
			filename = input;
		}
	}
}

/////////////////////////////////////////////////////////////////////////////
// CDialectApp

BEGIN_MESSAGE_MAP(CDialectApp, CWinApp)
	//{{AFX_MSG_MAP(CDialectApp)
	ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
	ON_COMMAND(ID_VIEW_SCRIPTPATH, OnViewScriptpath)
	ON_COMMAND(ID_HELP_FINDER, OnHelpFinder)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

#define ID_SPLASHTIMER	6468

class CAboutDlg : public CDialog
{
public:
	CAboutDlg();
	void DoSplash(CWnd* pParent);
	void SetVersionString();

// Dialog Data
	//{{AFX_DATA(CAboutDlg)
	enum { IDD = IDD_ABOUTBOX };
	//}}AFX_DATA

	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CAboutDlg)
	public:
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
	//}}AFX_VIRTUAL

// Implementation
protected:
	//{{AFX_MSG(CAboutDlg)
	afx_msg void OnTimer(UINT nIDEvent);
	virtual BOOL OnInitDialog();
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
	//{{AFX_DATA_INIT(CAboutDlg)
	//}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CAboutDlg)
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
	//{{AFX_MSG_MAP(CAboutDlg)
	ON_WM_TIMER()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

// App command to run the dialog
void CDialectApp::OnAppAbout()
{
	CAboutDlg aboutDlg;
	aboutDlg.DoModal();
}

BOOL CAboutDlg::OnInitDialog()
{
	CDialog::OnInitDialog();
	SetVersionString();
	return TRUE;  // return TRUE unless you set the focus to a control
	              // EXCEPTION: OCX Property Pages should return FALSE
}

void CAboutDlg::SetVersionString()
{
	CWnd* p = GetDlgItem(IDC_VERSION_STRING);
	if (p) {
		#if defined(DIALECT_LITE)
			CString Product = _T("Dialect Lite");
		#elif defined(DIALECT_STD)
			CString Product = _T("Dialect Standard");
		#elif defined(DIALECT_PRO)
			CString Product = _T("Dialect Professional");
		#elif defined(DIALECT_ENT)
			CString Product = _T("Dialect Enterprise");
		#endif
		CString text =
			Product + _T(" v1.21\n")
			_T("Build: ") + CString(__DATE__) + _T(" ") + CString(__TIME__) + _T("\n")
			_T("Copyright (C) 1998-2001 Aristar, Inc.\n")
			_T("Web:\thttp://dialect.sf.net\n")
			_T("PocketFrog Addition by:\tRevolvEvolution\n");
		p->SetWindowText(text);
	}
}

void CAboutDlg::DoSplash(CWnd* pParent)
{
	Create(CAboutDlg::IDD, pParent);
	if (pParent) {
		pParent->EnableWindow(FALSE);
		RECT prc, rc;
		GetWindowRect(&rc);
		pParent->GetWindowRect(&prc);
		int w = rc.right - rc.left;
		int h = rc.bottom - rc.top;
		rc.left = ((prc.right + prc.left) / 2) - (w / 2);
		rc.right = rc.left + w;
		rc.top = ((prc.bottom + prc.top) / 2) - (h / 2);
		rc.bottom = rc.top + h;
		MoveWindow(&rc);
	}
	SetVersionString();
	CWnd* pOK = GetDlgItem(IDOK);
	ShowWindow(SW_SHOW);
	BringWindowToTop();
	if (pOK) pOK->ShowWindow(SW_HIDE);
	SetTimer(ID_SPLASHTIMER, 2500, NULL);
}

void CAboutDlg::OnTimer(UINT nIDEvent)
{
	if (nIDEvent == ID_SPLASHTIMER) {
		KillTimer(ID_SPLASHTIMER);
		CWnd* pParent = GetParent();
		if (pParent) pParent->EnableWindow(TRUE);
		DestroyWindow();
	}
	else CDialog::OnTimer(nIDEvent);
}

/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// CDialectApp construction and Default I/O hacks...

#include "COutputDevice.h"
static CEditOutputDev* pOut = NULL;
extern bool InputBox(TString message, TString initValue, bool bPassword, TString& result);

#ifdef UNDER_CE
CDialectApp::CDialectApp(LPCTSTR lpszAppName, LPCTSTR lpszHelpName)
	: CWinApp(lpszAppName)//, lpszHelpName)
#else
CDialectApp::CDialectApp()
#endif
{
	// TODO: add construction code here,
	// Place all significant initialization in InitInstance
}

CDialectApp::~CDialectApp()
{
	// The app is being destroyed, free default output window
	if (pOut != NULL) delete pOut;
}

void CloseDefaultOut()
{
	if (pOut != NULL) {
		delete pOut;
		pOut = NULL;
	}
}

static void DefaultWrite(TString text)
{
	if (pOut == NULL) {
		pOut = new CEditOutputDev(&g_Interpreter);
	}
	pOut->TextOut(text);
}

static void DefaultWriteLine(TString text)
{
	DefaultWrite(text + _T("\r\n"));	// Standard edit window sequence
}

static TString DefaultRead(int count)
{
	// A count == -1 means read all available data
	// count >= 0 means read count number of bytes, chars, or whatever
	// I'm going to ignore that for now
	TString result;
	InputBox(_T("Please enter text..."), _T(""), false, result);
	return result;
}

static TString DefaultReadLine()
{
	TString result;
	InputBox(_T("Please enter text..."), _T(""), false, result);
	return result;
}

/////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////
// The one and only CDialectApp object

#ifdef UNDER_CE
	CDialectApp theApp(_T("Dialect"), _T("Dialect.htc"));
#else
	CDialectApp theApp;
#endif

void BackgroundHandler(CInterpreter* pInterp)
{
/*#ifdef UNDER_CE
	// Peek message was freezing on my HP-360LX!
    if (!theApp.PumpMessage()) {
        ::PostQuitMessage(0);
		pInterp->Abort();
		return;
	}
	//LONG lIdle = 0;
    //while (AfxGetApp()->OnIdle(lIdle++));
#else*/
	MSG msg;
	while (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
		if (!theApp.PumpMessage()) {
			::PostQuitMessage(0);
			pInterp->Abort();
			return;
		}
	}
	LONG lIdle = 0;
	while (AfxGetApp()->OnIdle(lIdle++));
//#endif
}

/////////////////////////////////////////////////////////////////////////////
// CDialectApp initialization

BOOL CDialectApp::InitInstance()
{
	// Standard initialization
	// If you are not using these features and wish to reduce the size
	//  of your final executable, you should remove from the following
	//  the specific initialization routines you do not need.

#ifndef UNDER_CE
#	ifdef _AFXDLL
		Enable3dControls();			// Call this when using MFC in a shared DLL
#	else
		Enable3dControlsStatic();	// Call this when linking to MFC statically
#	endif
#endif

	// Change the registry key under which our settings are stored.
	// TODO: You should modify this string to be something appropriate
	// such as the name of your company or organization.
	SetRegistryKey(_T("Aristar"));
#ifdef UNDER_CE
	LoadStdProfileSettings(3);
#else
	LoadStdProfileSettings(8);
#endif

	bool bAppendedFrame;
	{
		CWaitCursor cw;

		#ifdef DIALECT_ENT
			// So we can use the CLayoutWindow class to display layouts
			InitLayoutGlobals();
		#endif

		// Read command line args...
		//CCmdLine cmdLine;
		//#ifdef UNDER_CE
			ParseElCmdLineO(m_lpCmdLine);
		//#else
		//	ParseCommandLine(cmdLine);
		//#endif

		g_vCmdLineString = m_lpCmdLine;
		g_Interpreter.Reset();

		// Set the interpreter's default I/O
		g_Interpreter.SetDefaultRead(DefaultRead);
		g_Interpreter.SetDefaultReadLine(DefaultReadLine);
		g_Interpreter.SetDefaultWrite(DefaultWrite);
		g_Interpreter.SetDefaultWriteLine(DefaultWriteLine);
		g_Interpreter.SetBackgroundHandler(128, BackgroundHandler);
		//g_Interpreter.SetDebugOutFunc(DebugOut);

		bAppendedFrame = g_Interpreter.LoadAppendedFrame();
	}

	if (bAppendedFrame) {
		CString appDir;
		LPTSTR buf = appDir.GetBuffer(MAX_PATH);
		GetModuleFileName(NULL, buf, MAX_PATH);
		appDir.ReleaseBuffer();
		int p = appDir.ReverseFind('\\');
		if (p > 0) {
			appDir = appDir.Left(p + 1);
		}
		g_Interpreter.SetInitialPath(appDir);

		if (!g_Interpreter.ExecuteAppended()) {
			VALUE v = g_Interpreter.GetException();
			CString err = v[_T("msg")].String() + _T("\r\n") + v[_T("callstack")].String();
			MessageBox(NULL, err, _T("Error"), MB_OK);
		}
		while (g_Interpreter.GetOpenWindows() > 0) {
			if (!AfxGetApp()->PumpMessage()) {
				::PostQuitMessage(0);
				return FALSE;
			}
		}
		::PostQuitMessage(0);
		return FALSE;
	}
#ifndef IDE_SUPPORT
	else {
		CAboutDlg dlg;
		dlg.DoModal();
		::PostQuitMessage(0);
		return FALSE;
	}
#else
	CString InitialScript;
	CString ScriptDirectory = initScript;
	SplitFullPath(initScript, ScriptDirectory, InitialScript);

	if (!InitialScript.IsEmpty()) {
		// We just run this script file and exit
		g_Interpreter.SetInitialPath(ScriptDirectory);
		if (!g_Interpreter.InterpretFile(InitialScript)) {
			// Error
			VALUE v = g_Interpreter.GetException();
			CString err = v[_T("msg")].String() + _T("\r\n") + v[_T("callstack")].String();
			MessageBox(NULL, err, _T("Error"), MB_OK);
		}
		else {
			while (g_Interpreter.GetOpenWindows() > 0) {
				if (!AfxGetApp()->PumpMessage()) {
					::PostQuitMessage(0);
					return FALSE;
				}
			}
		}
		::PostQuitMessage(0);
		return FALSE;
	}

	// To create the main window, this code creates a new frame window
	// object and then sets it as the application's main window object.

	CMainFrame* pFrame = new CMainFrame;
	m_pMainWnd = pFrame;

	// create main frame window
	if (!pFrame->LoadFrame(IDR_MAINFRAME))
		return FALSE;

	// try to load shared MDI menus and accelerator table
#ifdef __MDI__
	HINSTANCE hInst = AfxGetResourceHandle();
	m_hMDIMenu  = ::LoadMenu(hInst, MAKEINTRESOURCE(IDR_DIALECTYPE));
	m_hMDIAccel = ::LoadAccelerators(hInst, MAKEINTRESOURCE(IDR_DIALECTYPE));
#endif

#ifndef UNDER_CE
	pFrame->DragAcceptFiles();
#endif

	// The main window has been initialized, so show and update it.
	pFrame->ShowWindow(m_nCmdShow);
	pFrame->UpdateWindow();

	if (!openMe.IsEmpty() && TFile::Exists(openMe)) {
		CString dir, file;
		SplitFullPath(openMe, dir, file);
		pFrame->OpenFile(dir, file);
	}
	else {
		#ifndef __MDI__
			pFrame->New();
		#endif
	}

	//static CAboutDlg dlg;	// 12/20/02  removed splash screen  Guilect
	//dlg.DoSplash(pFrame);  //  got tired of waiting that 2.5 seconds everytime

	return TRUE;
#endif
}

CDocument* CDialectApp::OpenDocumentFile(LPCTSTR lpszFileName)
{
#ifndef IDE_SUPPORT
	return NULL;
#else
	// Even though we aren't using the Document/View paradigm,
	// the MRU menu item still calls this function

	if (!TFile::Exists(lpszFileName)) {
		if (AfxGetMainWnd()) AfxGetMainWnd()->MessageBox(_T("File not found"), _T(""), MB_OK);
		return NULL;
	}

	CString dir, file;
	SplitFullPath(lpszFileName, dir, file);
	// If we return NULL, the file is removed from the MRU list
	// So we'll just make up something (it isn't used anyway)
	return ((CMainFrame*)m_pMainWnd)->OpenFile(dir, file) ? (CDocument*)1 : NULL;
#endif // IDE_SUPPORT
}

/////////////////////////////////////////////////////////////////////////////
// CDialectApp message handlers

int CDialectApp::ExitInstance()
{
#ifdef __MDI__
	if (m_hMDIMenu != NULL)
		FreeResource(m_hMDIMenu);
	if (m_hMDIAccel != NULL)
		FreeResource(m_hMDIAccel);
#endif

	return CWinApp::ExitInstance();
}

/////////////////////////////////////////////////////////////////////////////
// CDialectApp message handlers

CString CDialectApp::GetInitialPath()
{
	TCHAR buf[MAX_PATH];
	GetModuleFileName(NULL, buf, MAX_PATH);
	CString directory, filename;
	SplitFullPath(buf, directory, filename);
	return GetProfileString(_T("Options"), _T("ScriptPath"), directory);
}

void CDialectApp::OnViewScriptpath()
{
#ifdef IDE_SUPPORT
	// This should make it into a complete options dialog someday...
	TCHAR buf[MAX_PATH];
	GetModuleFileName(NULL, buf, MAX_PATH);
	CString directory, filename;
	SplitFullPath(buf, directory, filename);

	CInitPathDlg dlg(m_pMainWnd);
	dlg.m_Path = GetInitialPath();
	if (dlg.DoModal() == IDOK) {
		WriteProfileString(_T("Options"), _T("ScriptPath"), dlg.m_Path);
	}
#endif
}

void CDialectApp::OnHelpFinder()
{
#ifdef IDE_SUPPORT
	CString doc = GetProfileString(_T("Help"), _T("HTMLRefDoc"), _T("DialectRef.htm"));
	CString dir = GetProfileString(_T("Help"), _T("HTMLRefDir"), _T(""));
	if (dir.IsEmpty()) {
		TCHAR buf[MAX_PATH];
		GetModuleFileName(NULL, buf, MAX_PATH);
		dir = buf;
		int pos = dir.ReverseFind('\\');
		if (pos >= 0) {
			dir = dir.Left(pos + 1);
		}
	}

	if (dir[dir.GetLength() - 1] != '\\') dir += '\\';

	if (!TFile::Exists(dir + doc)) {
		AfxGetMainWnd()->MessageBox(_T("Failed to find help file: ") + dir + doc);
	}
	else {
		SHELLEXECUTEINFO sei;
		memset(&sei, 0, sizeof(SHELLEXECUTEINFO));
		sei.cbSize = sizeof(SHELLEXECUTEINFO);
#if defined(UNDER_CE) && (UNDER_CE > 200)
		sei.fMask = SEE_MASK_NOCLOSEPROCESS;
#endif
		sei.hwnd = AfxGetMainWnd()->GetSafeHwnd();
		sei.lpVerb = _T("open");
#ifdef UNDER_CE
		sei.lpFile = _T("\\Windows\\iexplore.exe");
		CString temp = _T("file://") + dir + doc;
		sei.lpParameters = temp;
#else
		sei.lpDirectory = dir;
		CString temp = dir + doc;
		sei.lpFile = temp;
#endif
		sei.nShow = SW_SHOW;
		if (!ShellExecuteEx(&sei)) {
			AfxGetMainWnd()->MessageBox(_T("Failed to open help file: ") + dir + doc);
		}
	}
#endif // IDE_SUPPORT
}
